package ro.upt.ac.cstaicu.instrumentation;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

import ro.upt.ac.cstaicu.analyze.Entity;
import ro.upt.ac.cstaicu.instrumentation.classadapters.CustomClassAdapter;

/**
 * It is an interface for the Instrumentation Module. The module uses ASM
 * (asm.ow2.org) to change the behavior of the class at runtime. It introduce
 * the ability for the class to log each predicate evaluation.
 * 
 * @author Cristian-Alexandru STAICU
 * 
 */
public class InstrumentationFacade implements Instrumentor {

	private Collection<Entity> predicates;

	/**
	 * It calls the instrumentClass method with an adapter created by the
	 * factory. The adapter should instrument all the methods in the class.
	 * 
	 * @param projectClassPath
	 *            - the path to the project containing the class to be
	 *            instrumented
	 * @param className
	 *            - the name of the class to be instrumented
	 * @return A {@link HashMap} that for each method name it associate a list
	 *         of predicate ids.
	 */
	@Override
	public byte[] tracePredicatesFromClass(
			String projectClassPath, String className) {

		ClassWriter writer = new ClassWriter(0);
		CustomClassAdapter adapter = AdapterFactory.getInstance()
				.getClassPredicateTracerAdapter(writer);
		return traceClass(projectClassPath, className, adapter, writer);

	}

	/**
	 * It calls the instrumentClass method with an adapter created by the
	 * factory. The adapter should instrument only the desired method.
	 * 
	 * @param projectClassPath
	 *            - the path to the project containing the class to be
	 *            instrumented
	 * @param desiredMethods
	 *            - a list with the names of the methods to be instrumented
	 * @param className
	 *            - the name of the class to be instrumented
	 * @return A {@link HashMap} that for each method name it associate a list
	 *         of predicate ids.
	 */
	@Deprecated
	public byte[] tracePredicatesFromMethod(String projectClassPath,
			String className, ArrayList<String> desiredMethods) {

		ClassWriter writer = new ClassWriter(0);
		CustomClassAdapter adapter = AdapterFactory.getInstance()
				.getMethodsFilterPredicateAdapter(writer, desiredMethods);
		traceClass(projectClassPath, className, adapter, writer);
		return writer.toByteArray();
	}

	//TODO javadoc comment
	/**
	 *
	 */
	private byte[] traceClass(
			String projectClassPath, 
			String className,
			CustomClassAdapter adapter,
			ClassWriter writer) {
		
		try {
		
			ClassReader reader = new ClassReader(
					new FileInputStream(projectClassPath + File.separator
							+ className.replace(".", File.separator) + ".class"));
			reader.accept(adapter, 0);

//			FileOutputStream fileWriter = new FileOutputStream(projectClassPath
//					+ File.separator + className.replace(".", File.separator)
//					+ ".class");

			
//			fileWriter.write(writer.toByteArray());
//			fileWriter.close();
//			return adapter.getTracedPredicates();
			predicates = adapter.getTracedPredicates();
			return writer.toByteArray();
		
		} catch (IOException e) {
			// TODO announce the user about the exception
			System.out.println(e);
			System.exit(1);
		}
		
		return null;
	}
	
	@Override
	public Collection<Entity> getTracedPredicates() {
		return predicates;
	}

}
